package com.eastedu.util;

import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoField;

/**
 * 中国时间(东八区)工具类
 * <p>
 * 服务器系统时间通常为0时区
 * 但由于历史原因，程序及数据存储，使用的东八区时间戳
 *
 * @author 张志帅
 */
public interface ChineseTimeUtil {


    /**
     * The constant MESSAGE.
     */
    String MESSAGE = "anyTimeOfMonth must not be null";
    /**
     * The constant MESSAGE1.
     */
    String MESSAGE1 = "pattern must not be null or empty";

    /**
     * Now time long.
     *
     * @return the long
     */
    static Long nowTime() {
        return parseMilli(now());
    }

    /**
     * Now local date time.
     *
     * @return the local date time
     */
    static LocalDateTime now() {
        return LocalDateTime.now(east8ZoneId());
    }

    /**
     * Current time of monday long.
     *
     * @return the long
     */
    static Long mondayOfWeek() {
        LocalDate localDate = LocalDate.now(east8ZoneId()).with(DayOfWeek.MONDAY);
        return parseMilli(localDate.atStartOfDay());
    }

    /**
     * Begin time of month local date time.
     *
     * @param anyTimeOfMonth the any time of month
     * @return the local date time
     */
    static LocalDateTime beginTimeOfMonth(LocalDateTime anyTimeOfMonth) {
        Assert.notNull(anyTimeOfMonth, MESSAGE);
        return anyTimeOfMonth.with(LocalTime.MIN).withDayOfMonth(1);
    }

    /**
     * End time of month local date time.
     *
     * @param anyTimeOfMonth the any time of month
     * @return the local date time
     */
    static LocalDateTime endTimeOfMonth(LocalDateTime anyTimeOfMonth) {
        Assert.notNull(anyTimeOfMonth, MESSAGE);
        return anyTimeOfMonth.with(LocalTime.MAX).withDayOfMonth(daysOfMonth(anyTimeOfMonth));
    }

    /**
     * Begin time of previous month local date time.
     *
     * @param anyTimeOfMonth the any time of month
     * @return the local date time
     */
    static LocalDateTime beginTimeOfPreviousMonth(LocalDateTime anyTimeOfMonth) {
        Assert.notNull(anyTimeOfMonth, MESSAGE);
        return beginTimeOfMonth(anyTimeOfMonth).minusMonths(1);
    }

    /**
     * End time of previous month local date time.
     *
     * @param anyTimeOfMonth the any time of month
     * @return the local date time
     */
    static LocalDateTime endTimeOfPreviousMonth(LocalDateTime anyTimeOfMonth) {
        Assert.notNull(anyTimeOfMonth, MESSAGE);
        return endTimeOfMonth(beginTimeOfPreviousMonth(anyTimeOfMonth));
    }

    /**
     * Days of month int.
     *
     * @param anyTimeOfMonth the any time of month
     * @return the int
     */
    static int daysOfMonth(LocalDateTime anyTimeOfMonth) {
        Assert.notNull(anyTimeOfMonth, MESSAGE);
        ChronoField chronoField = ChronoField.DAY_OF_MONTH;
        return (int) anyTimeOfMonth.range(chronoField).getMaximum();
    }

    /**
     * Begin time of current month local date time.
     *
     * @return the local date time
     */
    static LocalDateTime beginTimeOfCurrentMonth() {
        return beginTimeOfMonth(now());
    }

    /**
     * End time of current month local date time.
     *
     * @return the local date time
     */
    static LocalDateTime endTimeOfCurrentMonth() {
        return endTimeOfMonth(now());
    }

    /**
     * Begin time of previous month local date time.
     *
     * @return the local date time
     */
    static LocalDateTime beginTimeOfPreviousMonth() {
        return beginTimeOfPreviousMonth(now());
    }

    /**
     * End time of previous month local date time.
     *
     * @return the local date time
     */
    static LocalDateTime endTimeOfPreviousMonth() {
        return endTimeOfPreviousMonth(now());
    }

    /**
     * Parse local date time.
     *
     * @param epochSecond the epoch second
     * @return the local date time
     */
    static LocalDateTime parse(long epochSecond) {
        return LocalDateTime.ofInstant(Instant.ofEpochSecond(epochSecond), east8ZoneId());
    }

    /**
     * Parse milli local date time.
     *
     * @param epochMilli the epoch milli
     * @return the local date time
     */
    static LocalDateTime parseMilli(long epochMilli) {
        return LocalDateTime.ofInstant(Instant.ofEpochMilli(epochMilli), east8ZoneId());
    }

    /**
     * Parse long.
     *
     * @param localDateTime the local date time
     * @return the long
     */
    static long parse(LocalDateTime localDateTime) {
        Assert.notNull(localDateTime, "localDateTime must not be null");
        return localDateTime.toInstant(east8ZoneOffset()).getEpochSecond();
    }

    /**
     * Parse milli long.
     *
     * @param localDateTime the local date time
     * @return the long
     */
    static long parseMilli(LocalDateTime localDateTime) {
        Assert.notNull(localDateTime, "localDateTime must not be null");
        return localDateTime.toInstant(east8ZoneOffset()).toEpochMilli();
    }


    /**
     * Parse local date time.
     *
     * @param time    the time
     * @param pattern the pattern
     * @return the local date time
     */
    static LocalDateTime parse(String time, String pattern) {
        Assert.hasText(time, "time must not be null or empty");
        Assert.hasText(pattern, MESSAGE1);
        DateTimeFormatter df = DateTimeFormatter.ofPattern(pattern);
        return LocalDateTime.from(df.parse(time));
    }

    /**
     * Format string.
     *
     * @param localDateTime the local date time
     * @param pattern       the pattern
     * @return the string
     */
    static String format(LocalDateTime localDateTime, String pattern) {
        Assert.notNull(localDateTime, "localDateTime must not be null");
        Assert.hasText(pattern, MESSAGE1);
        DateTimeFormatter df = DateTimeFormatter.ofPattern(pattern);
        return df.format(localDateTime);
    }

    /**
     * Format string.
     *
     * @param time    the time
     * @param pattern the pattern
     * @return the string
     */
    static String format(Long time, String pattern) {
        if (ObjectUtils.isEmpty(time)) {
            return "";
        }
        Assert.hasText(pattern, MESSAGE1);
        LocalDateTime localDateTime = parseMilli(time);
        DateTimeFormatter df = DateTimeFormatter.ofPattern(pattern);
        return df.format(localDateTime);
    }

    /**
     * Format string.
     *
     * @param time the time
     * @return 默认样式 yyyy-MM-dd'T'HH:mm:ss
     */
    static String formatWithDefault(Long time) {
        if (ObjectUtils.isEmpty(time)) {
            return "";
        }
        return format(time, "yyyy-MM-dd'T'HH:mm:ss");
    }

    /**
     * East 8 zone id zone id.
     *
     * @return the zone id
     */
    static ZoneId east8ZoneId() {
        return ZoneId.of("Asia/Shanghai");
    }

    /**
     * East 8 zone offset zone offset.
     *
     * @return the zone offset
     */
    static ZoneOffset east8ZoneOffset() {
        return ZoneOffset.of("+8");
    }


    /**
     * Gets year month first day string.
     *
     * @param localDate the local date
     * @return the year month first day string
     */
    static String getYearMonthFirstDayString(LocalDate localDate) {
        String date = localDate.toString();
        String[] result = date.split("-");
        return String.format("%s-%s-01", result[0], result[1]);
    }

    /**
     * Gets year month day string.
     *
     * @param localDate the local date
     * @return the year month day string
     */
    static String getYearMonthDayString(LocalDate localDate) {
        String date = localDate.toString();
        String[] result = date.split("-");
        return String.format("%s/%s/%s", result[0], result[1], result[2]);
    }

    /**
     * Gets year month string.
     *
     * @param yearMonthTime the year month time
     * @return the year month string
     */
    static String getYearMonthString(Long yearMonthTime) {
        return format(yearMonthTime, "yyyy-MM");
    }

    /**
     * 格式化当前时间
     *
     * @param pattern 样式
     * @return 格式化后的内容 string
     */
    static String formatWithCurrentTime(String pattern) {
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        return now.format(formatter);
    }

    /**
     * 将字符串解析成 LocalDateTime
     *
     * @param dateString 支持两种字符串日期格式  1.2029-06-05T15:38:10    2.2029-06-05
     * @return the local date time
     */
    static Long parseMilli(String dateString) {
        try {
            return parseMilli(getLocalDateTime(dateString));
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Gets local date time.
     *
     * @param dateString the date string
     * @return the local date time
     */
    static LocalDateTime getLocalDateTime(String dateString) {
        LocalDateTime date;
        try {
            date = LocalDateTime.parse(dateString, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
        } catch (Exception e) {
            try {
                date = LocalDateTime.parse(dateString + "T00:00:00", DateTimeFormatter.ISO_LOCAL_DATE_TIME);
            } catch (Exception e1) {
                date = LocalDateTime.parse(dateString, DateTimeFormatter.ISO_LOCAL_DATE);
            }
        }
        return date;
    }
}
